﻿using MovingChame.Web.Helpers;
using MovingChame.Web.Models;
using System;
using System.Collections.Generic;
using System.Data.Spatial;
using System.Linq;
using System.Web;

namespace MovingChame.Web.Infrastructure
{
    public class TourProvider : ITourProvider
    {
        #region Static Variable
        private static TourProvider current;
        public static TourProvider Current
        {
            get
            {
                if (current == null) current = new TourProvider();
                return current;
            }
        }
        #endregion

        #region Configuration
        private string applicationName;
        public string ApplicationName
        {
            get { return applicationName; }
            set { applicationName = value; }
        }

        private string connectionStringName;
        private string providerName;
        private MovingChameModelContainer db;

        public void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
        {
            providerName = name;

            if (config["applicationName"] != null)
                applicationName = config["applicationName"];

            if (config["connectionStringName"] != null)
            {
                connectionStringName = config["connectionStringName"];
                db = new MovingChameModelContainer();
                db.Database.Connection.ConnectionString = ConfigurationHelper.GetConnectionString(connectionStringName);
            }
            else
                db = new MovingChameModelContainer();
        }

        private TourProvider()
        {
            Initialize("TourProvider", new System.Collections.Specialized.NameValueCollection());
        }
        #endregion

        #region Interfact Methods - Add, remove, update
        public int AddTour(string name, int tourLength, TourType tourType, string summary, string description, int budget, int creatorId)
        {
            var Tour = new Tour
            {
                Name = name,
                TourLength = tourLength,
                Type = tourType,
                Summary = summary,
                Description = description,
                Status = TourStatus.Creating,
                Budget = budget,
                UserId = creatorId,
                AddedDate = DateTime.UtcNow,
                Rating = 0
            };

            db.Tours.Add(Tour);
            db.SaveChanges();

            return Tour.Id;
        }

        public bool UpdateTour(int tourId, Tour updatedTour)
        {
            Tour tour = GetTourById(tourId);
            if (tour == null)
                return false;
            if (tour.Status != TourStatus.Creating)
                return false;
            db.Entry(tour).CurrentValues.SetValues(updatedTour);
            db.SaveChanges();
            return true;
        }

        public bool ActivateTour(int tourId)
        {
            var updatedTour = db.Tours.SingleOrDefault(o => o.Id == tourId);
            updatedTour.Status = TourStatus.Created;
            updatedTour.AddedDate = DateTime.UtcNow;
            updatedTour.Schedule = updatedTour.Schedules.OrderBy(o => o.DateOrder).ThenBy(o => o.StartTime).FirstOrDefault();
            db.SaveChanges();
            return true;
        }

        public bool RemoveTour(int tourId)
        {
            var tour = db.Tours.SingleOrDefault(o => o.Id == tourId);
            if (tour == null)
                return false;
            db.Tours.Remove(tour);
            db.SaveChanges();
            return true;
        }
        #endregion

        #region Interface Methods - Search
        public IQueryable<Tour> SearchToursInName(string keyword)
        {
            return db.Tours.Where(o => o.Name.Contains(keyword) && o.Status == TourStatus.Created);
        }

        public IQueryable<Tour> SearchToursInSummary(string keyword)
        {
            return db.Tours.Where(o => o.Summary.Contains(keyword) && o.Status == TourStatus.Created);
        }

        public IQueryable<Tour> SearchTours(string keyword)
        {
            IQueryable<Tour> result = Enumerable.Empty<Tour>().AsQueryable();
            result = result.Union(SearchToursInSummary(keyword));
            result = result.Union(SearchToursInName(keyword));
            return result;
        }
        #endregion

        #region Interface Methods - Get
        public Tour GetTourById(int tourId)
        {
            return db.Tours.SingleOrDefault(o => o.Id == tourId);
        }

        public IQueryable<Tour> GetToursByType(double latitude, double longitude, TourType tourType)
        {
            DbGeography center = DbGeography.FromText("POINT(" + longitude.ToString() + " " + latitude.ToString() + ")");

            return db.Tours.Where(o => o.Status == TourStatus.Created && o.Type == tourType &&
                DbGeographyHelper.GeographyInArea(center, Constants.AreaMetric, o.Schedule.Location.Center));
        }

        public IQueryable<Tour> GetToursByCreator(int creatorId)
        {
            return db.Tours.Where(o => o.UserId == creatorId);
        }

        public IQueryable<Tour> GetPrivateToursByCreator(int creatorId)
        {
            return db.Tours.Where(o => o.UserId == creatorId && o.Status == TourStatus.Creating);
        }

        public IQueryable<Tour> GetPublicToursByCreator(int creatorId)
        {
            return db.Tours.Where(o => o.UserId == creatorId && o.Status == TourStatus.Created);
        }

        public IQueryable<Tour> GetPublicTours(double latitude, double longitude)
        {
            DbGeography center = DbGeography.FromText("POINT(" + longitude.ToString() + " " + latitude.ToString() + ")");

            return db.Tours.Where(o => o.Status == TourStatus.Created &&
                DbGeographyHelper.GeographyInArea(center, Constants.AreaMetric, o.Schedule.Location.Center));
        }

        public IQueryable<Tour> GetPublicToursByDetail(double latitude, double longitude, List<TourType> types, int length, int budget)
        {
            DbGeography center = DbGeography.FromText("POINT(" + longitude.ToString() + " " + latitude.ToString() + ")");

            return db.Tours.Where(o => types.Contains(o.Type) && o.TourLength == length && o.Budget <= budget &&
                DbGeographyHelper.GeographyInArea(center, Constants.AreaMetric, o.Schedule.Location.Center));
        }
        #endregion

        #region Interface Methods - Vote Tour
        public bool VoteUpTour(int tourId)
        {
            Tour tour = GetTourById(tourId);
            if (tour == null)
                return false;
            if (tour.Status != TourStatus.Creating)
                return false;
            tour.Rating = tour.Rating + 1;
            db.SaveChanges();
            return true;
        }

        public bool VoteDownTour(int tourId)
        {
            Tour tour = GetTourById(tourId);
            if (tour == null)
                return false;
            if (tour.Status != TourStatus.Creating)
                return false;
            tour.Rating = tour.Rating - 1;
            db.SaveChanges();
            return true;
        }
        #endregion
    }
}